#pragma once
#include <ostream>
#include <cmath>


//二维向量
template<typename T>
class Vector2{
public:
    T x, y;
    Vector2() : x(), y() {};
    Vector2(T _n) : x(_n), y(_n) {};
    Vector2(T _x, T _y) : x(_x), y(_y) {};

    Vector2<T>& operator=(const Vector2<T> &v) {
        x = v.x, y = v.y;
        return *this;
    }

    Vector2<T> operator+(const Vector2<T> &v) const{
        return Vector2(x + v.x, y + v.y);
    }

    Vector2<T>& operator+=(const Vector2<T> &v){
        x += v.x, y += v.y;
        return *this;
    }

    Vector2<T> operator-(const Vector2<T> &v) const{
        return Vector2(x - v.x, y - v.y);
    }

    Vector2<T>& operator-=(const Vector2<T> &v){
        x -= v.x, y -= v.y;
        return *this;
    }

    Vector2<T> operator-() const{
        return Vector2<T>(-x, -y);
    }

    template<typename U>
    Vector2<T> operator*(U u) const{
        return Vector2<T>(x * u, y * u);
    }

    Vector2<T> operator*(const Vector2<T> &v) const{
        return Vector2<T>(x * v.x, y * v.y);
    }

    template<typename U>
    Vector2<T>& operator*=(U u){
        x *= u, y *= u;
        return *this;
    }

    template<typename U>
    Vector2<T> operator/(U u) const{
        return Vector2<T>(x / u, y / u);
    }

    template<typename U>
    Vector2<T>& operator/=(U u){
        x /= u, y /= u;
        return *this;
    }

    friend std::ostream& operator<<(std::ostream& os, const Vector2<T>& v) {
		return os << "(" << v.x << ", " << v.y << ")";
	}

	friend Vector2<T> operator*(const T& r, const Vector2<T>& v) {
		return v *r;
	}

    float Norm() const{
        return x * x + y * y;
    }

    float Length() const{
        return std::sqrt(Norm());
    }

    Vector2<T>& Normalize(){
        *this /= Length();
        return *this;
    }
};

template<typename T>
inline T dotProduct(const Vector2<T>& v1, const Vector2<T>& v2) {
    return (v1.x * v2.x + v1.y * v2.y);
}

template<typename T>
inline Vector2<T> lerp(const Vector2<T>& v1, const Vector2<T>& v2, const float& t) {
    return v1 * (1 - t) + v2 * t;
}


//三维向量
template<typename T>
class Vector3{
public:
    T x, y, z;
    Vector3() : x(), y(), z() {};
    Vector3(T _n) : x(_n), y(_n), z(_n) {};
    Vector3(T _x, T _y, T _z) : x(_x), y(_y), z(_z) {};

    Vector3<T>& operator=(const Vector3<T> &v) {
        x = v.x, y = v.y, z = v.z;
        return *this;
    }

    Vector3<T> operator+(const Vector3<T> &v) const{
        return Vector3(x + v.x, y + v.y, z + v.z);
    }

    Vector3<T>& operator+=(const Vector3<T> &v){
        x += v.x, y += v.y, z += v.z;
        return *this;
    }

    Vector3<T> operator-(const Vector3<T> &v) const{
        return Vector3(x - v.x, y - v.y, z - v.z);
    }

    Vector3<T>& operator-=(const Vector3<T> &v){
        x -= v.x, y -= v.y, z -= v.z;
        return *this;
    }

    Vector3<T> operator-() const{
        return Vector3<T>(-x, -y, -z);
    }

    template<typename U>
    Vector3<T> operator*(U u) const{
        return Vector3<T>(x * u, y * u, z * u);
    }

    Vector3<T> operator*(const Vector3<T> &v) const{
        return Vector3<T>(x * v.x, y * v.y, z * v.z);
    }

    template<typename U>
    Vector3<T>& operator*=(U u){
        x *= u, y *= u, z *= u;
        return *this;
    }

    template<typename U>
    Vector3<T> operator/(U u) const{
        return Vector3<T>(x / u, y / u, z / u);
    }

    template<typename U>
    Vector3<T>& operator/=(U u){
        x /= u, y /= u, z /= u;
        return *this;
    }

    friend std::ostream& operator<<(std::ostream& os, const Vector3<T>& v) {
		return os << "(" << v.x << ", " << v.y << ", " << v.z << ")";
	}

	friend Vector3<T> operator*(const T& r, const Vector3<T>& v) {
		return v *r;
	}

    float Norm() const{
        return x * x + y * y + z * z;
    }

    float Length() const{
        return std::sqrt(Norm());
    }

    Vector3<T>& Normalize(){
        *this /= Length();
        return *this;
    }
};

template<typename T>
inline T dotProduct(const Vector3<T>& v1, const Vector3<T>& v2) {
    return (v1.x * v2.x + v1.y * v2.y + v1.z * v2.z);
}

template<typename T>
inline Vector3<T> crossProduct(const Vector3<T>& v1, const Vector3<T>& v2) {
    return Vector3(v1.y * v2.z - v1.z * v2.y, v1.z * v2.x - v1.x * v2.z, v1.x * v2.y - v1.y * v2.x);
}

template<typename T>
inline Vector3<T> lerp(const Vector3<T>& v1, const Vector3<T>& v2, const float& t) {
    return v1 * (1 - t) + v2 * t;
}

//四维向量
template<typename T>
class Vector4{
public:
    T x, y, z, w;
    Vector4() : x(), y(), z(), w() {};
    Vector4(T _n) : x(_n), y(_n), z(_n), w(_n) {};
    Vector4(T _x, T _y, T _z, T _w) : x(_x), y(_y), z(_z), w(_w) {};

    Vector4<T>& operator=(const Vector4<T> &v) {
        x = v.x, y = v.y, z = v.z, w = v.w;
        return *this;
    }

    Vector4<T> operator+(const Vector4<T> &v) const{
        return Vector4(x + v.x, y + v.y, z + v.z, w + v.w);
    }

    Vector4<T>& operator+=(const Vector4<T> &v){
        x += v.x, y += v.y, z += v.z, w += v.w;
        return *this;
    }

    Vector4<T> operator-(const Vector4<T> &v) const{
        return Vector4(x - v.x, y - v.y, z - v.z, w - v.w);
    }

    Vector4<T>& operator-=(const Vector4<T> &v){
        x -= v.x, y -= v.y, z -= v.z, w -= v.w;
        return *this;
    }

    Vector4<T> operator-() const{
        return Vector4<T>(-x, -y, -z, -w);
    }

    template<typename U>
    Vector4<T> operator*(U u) const{
        return Vector4<T>(x * u, y * u, z * u, w * u);
    }

    Vector4<T> operator*(const Vector4<T> &v) const{
        return Vector4<T>(x * v.x, y * v.y, z * v.z, w * v.w);
    }

    template<typename U>
    Vector4<T>& operator*=(U u){
        x *= u, y *= u, z *= u, w *= u;
        return *this;
    }

    template<typename U>
    Vector4<T> operator/(U u) const{
        return Vector4<T>(x / u, y / u, z / u, w / u);
    }

    template<typename U>
    Vector4<T>& operator/=(U u){
        x /= u, y /= u, z /= u, w /= u;
        return *this;
    }

    friend std::ostream& operator<<(std::ostream& os, const Vector4<T>& v) {
		return os << "(" << v.x << ", " << v.y << ", " << v.z << ", " << v.w << ")";
	}

	friend Vector4<T> operator*(const T& r, const Vector4<T>& v) {
		return v *r;
	}

    float Norm() const{
        return x * x + y * y + z * z + w * w;
    }

    float Length() const{
        return std::sqrt(Norm());
    }

    Vector4<T>& Normalize(){
        *this /= Length();
        return *this;
    }
};

template<typename T>
inline T dotProduct(const Vector4<T>& v1, const Vector4<T>& v2) {
    return (v1.x * v2.x + v1.y * v2.y + v1.z * v2.z + v1.w * v2.w);
}

template<typename T>
inline Vector4<T> lerp(const Vector4<T>& v1, const Vector4<T>& v2, const float& t) {
    return v1 * (1 - t) + v2 * t;
}

//三维矩阵
class Mat3{
public:
    float m[3][3];
    Mat3();
    Mat3(float mat[3][3]);
    Mat3(float t00, float t01, float t02,
         float t10, float t11, float t12,
         float t20, float t21, float t22);

    template <typename T>
    Mat3(Vector3<T> v0, Vector3<T> v1, Vector3<T> v2);
    Mat3 operator+(const Mat3 &m) const;

    template <typename T>
    Vector3<T> operator*(const Vector3<T>& v) const {
        return Vector3<T>(
            m[0][0]*v.x + m[0][1]*v.y + m[0][2]*v.z,
            m[1][0]*v.x + m[1][1]*v.y + m[1][2]*v.z,
            m[2][0]*v.x + m[2][1]*v.y + m[2][2]*v.z
        );
    }
    Mat3 operator*(const Mat3 &m) const;
    Mat3 operator*(float n) const;
    Mat3 &Transpose();
    float Determinant();

    friend std::ostream &operator<<(std::ostream &os, const Mat3 &v);
};

//四维矩阵
class Mat4{
public:
    float m[4][4];
    Mat4();
    Mat4(float mat[4][4]);
    Mat4(float t00, float t01, float t02, float t03,
         float t10, float t11, float t12, float t13,
         float t20, float t21, float t22, float t23,
         float t30, float t31, float t32, float t33);
    template<typename T>
    Mat4(Vector4<T> v0, Vector4<T> v1, Vector4<T> v2, Vector4<T> v3) {
        m[0][0] = v0.x, m[0][1] = v1.x, m[0][2] = v2.x, m[0][3] = v3.x;
        m[1][0] = v0.y, m[1][1] = v1.y, m[1][2] = v2.y, m[1][3] = v3.y;
        m[2][0] = v0.z, m[2][1] = v1.z, m[2][2] = v2.z, m[2][3] = v3.z;
        m[3][0] = v0.w, m[3][1] = v1.w, m[3][2] = v2.w, m[3][3] = v3.w;
    };

    template<typename T>
    Vector4<T> operator*(const Vector4<T>& v) const {
        return Vector4<T>(
            m[0][0] * v.x + m[0][1] * v.y + m[0][2] * v.z + m[0][3] * v.w,
            m[1][0] * v.x + m[1][1] * v.y + m[1][2] * v.z + m[1][3] * v.w,
            m[2][0] * v.x + m[2][1] * v.y + m[2][2] * v.z + m[2][3] * v.w,
            m[3][0] * v.x + m[3][1] * v.y + m[3][2] * v.z + m[3][3] * v.w
        );
    }

    Mat4 operator*(const Mat4 &m) const;
    Mat4 operator*(float s) const;
    friend std::ostream &operator<<(std::ostream &os, const Mat4 &v);

    Mat4 &Transpose();
    float Determinant() const;
    float Minor(int row, int col) const;
    Mat4 Inverse() const;
};


